using System;
using System.Reflection;
using SwinGame;
using Color = System.Drawing.Color;
using System.Collections.Generic;

namespace solid_test.src
{
    public static class ResourceManager
    {
        public static List<Bitmap> _bitmaps = new List<Bitmap>();
        public static List<string> _spritePath = new List<string>();
        public static void LoadBitmap(string path)
        {
            _bitmaps.Add(new Bitmap(path));
            _spritePath.Add(path);
        }
        public static Bitmap getBitmap(string path)
        {
            for (int i = 0; i < _spritePath.Count; i++)
            {
                if (_spritePath[i] == path) { return _bitmaps[i]; }
            }
            Console.WriteLine("Couldn't get bitmap");
            return null;
        }
    }

    public interface IEntityPosition
    {
        void setPosition(float x, float y, bool relative = false);
        float[] getPosition();
    }

    //modifies unit position//
    public class UnitPosition : IEntityPosition
    {
        float _x = 0;
        float _y = 0;
        public void setPosition(float x, float y, bool relative = false)
        {
            if (relative) { _x += x; _y += y; Console.WriteLine(_x + " " + _y); }
            else{ _x = x; _y = y;}
        }
        public float[] getPosition() { return new float[2] { _y, _x }; }
        public UnitPosition(float x, float y) { setPosition(x, y, false); }
    }

    public interface IEntityStat
    {
        void setMax(float maxVal);
        void set(float data);
        float getMax();
        float get();
    }

    public class Health : IEntityStat
    {
        float _health;
        float _maxHealth;

        public void set(float health) { _health = health; }
        public void setMax(float maxHealth) { _maxHealth = maxHealth; }
        public float get() { return _health; }
        public float getMax() { return _maxHealth; }
    }

    public class Mana : IEntityStat
    {
        float   _mana;
        float   _maxMana;

        public void set(float mana) { _mana = mana; }
        public void setMax(float maxMana) { _maxMana = maxMana; }
        public float get() { return _mana; }
        public float getMax() { return _maxMana; }
    }

    public interface IEntitySprite
    {
        void setSprite(string filename);
        Sprite getSprite();
        void DrawSprite(int x, int y);
    }
    public class UnitSprite : IEntitySprite
    {
        Sprite  _sprite;

        public void setSprite(string filename) { _sprite = new Sprite(ResourceManager.getBitmap(filename));}//new Bitmap(filename)); }
        public Sprite getSprite() { return _sprite; }
        public void DrawSprite(int x, int y) { _sprite.Draw(x,y); }
        public UnitSprite(string path) { setSprite(path); }
    }


    public interface IEntityModel
    {
        void Update(double dt);
        void Move(float x, float y);//
    }

    public class UnitModel : IEntityModel
    {
        IEntityPosition _currentPos;
        IEntitySprite _sprite;

        public void Move(float x, float y) { _currentPos.setPosition(x, y, true); }

        public void Update(double dt)
        {
            _sprite.DrawSprite((int)_currentPos.getPosition()[1], (int)_currentPos.getPosition()[0]);
        }

        public UnitModel(float x, float y, string sprite_path)
        {
            _currentPos = new UnitPosition(x, y);
            _sprite = new UnitSprite(sprite_path);
        }
    }

    public interface IEntityController
    {
        void getInput();
    }

    public class PlayerUnitController : IEntityController
    {
        IEntityModel _model;

        public void getInput()
        {
            if (SwinGame.Input.KeyDown(KeyCode.vk_LEFT)){_model.Move(-1, 0);}
            if (SwinGame.Input.KeyDown(KeyCode.vk_RIGHT)){_model.Move(1, 0);}
            if (SwinGame.Input.KeyDown(KeyCode.vk_UP)) { _model.Move(0, -1); }
            if (SwinGame.Input.KeyDown(KeyCode.vk_DOWN)) { _model.Move(0, 1); }
            //if paused? dt 
            _model.Update(1);
        }
        public PlayerUnitController(){_model = new UnitModel(0,0,"alpha.png");}

    }
    public class AiUnitController : IEntityController
    {
        IEntityModel _model;
        Random r = new Random();
        public void getInput()
        {
            if (r.Next(10) < 5) { _model.Move(-0.1f, 0); }
            if (r.Next(10) < 5) { _model.Move(0.1f, 0); }
            if (r.Next(10) < 5) { _model.Move(0, -0.1f); }
            if (r.Next(10) < 5) { _model.Move(0, 0.1f); }
            //if paused? dt 
            _model.Update(1);
        }
        public AiUnitController() { _model = new UnitModel(0, 0, "alpha.png"); }

    }

    public interface IEntityView
    {
        void SeeUser();
    }

    public class UnitView : IEntityView
    {
        IEntityController _controller;

        public void SeeUser()
        {
            _controller.getInput();
        }

        public UnitView(IEntityController controller) { _controller = controller; }

    }

    public class player_controller_marble : controller_marble
    {
        public player_controller_marble(model_marble model) : base(model) { }


        public void poll_input()
        {
            int dx = 0;
            int dy = 0;
            if (Input.KeyDown(KeyCode.vk_LEFT)) { dx--; }
            if (Input.KeyDown(KeyCode.vk_RIGHT)) { dx++; }
            if (Input.KeyDown(KeyCode.vk_UP)) { dy--; }
            if (Input.KeyDown(KeyCode.vk_DOWN)) { dy++; }
            SendRollEvent(dx, dy);
        }
    }

    public class controller_marble
    {
        #region Event Data - //data for EventHandlers//

        public class RollEventArgs : EventArgs
        {
            public double _fx;
            public double _fy;
            public RollEventArgs(double fx, double fy) { _fx = fx; _fy = fy; }
        }

        #endregion

        #region EventHandlers - //create event handlers for model updates//

        public event EventHandler<RollEventArgs> RollEvent;

        #endregion

        #region Actions - //sent to model//

        public void SendRollEvent(double fx, double fy) { RollEvent(this, new RollEventArgs(fx, fy)); }

        #endregion

        public void set_target_model(model_marble model)
        {
            RollEvent += model.RollEvent;
        }

        public controller_marble(model_marble model)
        {
            set_target_model(model);
        }
    }




    public class model_marble
    {
        #region Fields

        double _velx = 0;       double _vely = 0;
        double _x = 0;          double _y = 0;
        double _mass = 200.0;   double _friction = 1.01;

        #endregion

        #region Accessors - //sends position change to view//

        double Y
        {
            set
            {
                if (value != _y)
                {
                    YChanged(this, new YChangeEventArgs(value));
                }
                _y = value;
            }
            get 
            {
                return _y;
            }
        }
        double X
        {
            set 
            {
                if (value != _x)
                { 
                    XChanged(this, new XChangeEventArgs(value)); 
                }
                _x = value;
            }
            get 
            {
                return _x; 
            }
        }

        #endregion

        #region EventData - //data for event handlers//

        public class XChangeEventArgs : EventArgs
        {
            public double _x;
            public XChangeEventArgs(double x) { this._x = x; }
        }

        public class YChangeEventArgs : EventArgs
        {
            public double _y;
            public YChangeEventArgs(double y) { this._y = y; }
        }

        #endregion  

        #region Event Reactions - //responds to controller//

        //gets message from controller//
        public void RollEvent(object sender, controller_marble.RollEventArgs rea)
        {
                _velx += rea._fx / _mass;
                _vely += rea._fy / _mass;
        }

        #endregion

        public void update()
        {
            X = X + _velx; Y = Y + _vely;                           //update position
            if (_velx != 0) { _velx /= _friction; }                 //update velocity
            if (_vely != 0) { _vely /= _friction; }                 //clip velocity
            if (_velx < 0.001 && _velx > -0.001) { _velx = 0; }
            if (_vely < 0.001 && _vely > -0.001) { _vely = 0; }
        }

        #region Event Handlers - //create handlers for view updates//

        public event EventHandler<XChangeEventArgs> XChanged;
        public event EventHandler<YChangeEventArgs> YChanged;

        #endregion

        public model_marble() {}
    }

    public class view_marble
    {
        #region Fields

        Color _col = Color.Aqua;
        int _x = 0;  int _y = 0;
        int _size = 9;

        #endregion

        public void draw(){ Graphics.DrawCircle(_col, _x, _y, _size);}

        #region Event Reactions

        void XChanged(object sender, model_marble.XChangeEventArgs xe){_x = (int)xe._x;}
        void YChanged(object sender, model_marble.YChangeEventArgs ye){ _y = (int)ye._y; }

        #endregion

        public void set_target_model(model_marble model)
        {
            model.XChanged += XChanged;
            model.YChanged += YChanged;
        }

        public view_marble(model_marble model)
        {
            set_target_model(model);
        }
    }

    public class Unit
    {
        
    }

    public class GameMain
    {
        public static void Main()
        {
            //Start the audio system so sound can be played
            Audio.OpenAudio();
            ResourceManager.LoadBitmap("alpha.png");

           
            model_marble mModel = new model_marble();
            player_controller_marble mController = new player_controller_marble(mModel);
            view_marble mView = new view_marble(mModel);


            //Open the game window
            Graphics.OpenGraphicsWindow("GameMain", 800, 600);
            Graphics.ShowSwinGameSplashScreen();

            //Run the game loop
            while (false == Input.WindowCloseRequested())
            {
                //Fetch the next batch of UI interaction
                Input.ProcessEvents();
                mController.poll_input();
                mModel.update();
                //Clear the screen and draw the framerate
                Graphics.ClearScreen(Color.White);
                mView.draw();

                Text.DrawFramerate(0, 0);

                //Draw onto the screen
                Graphics.RefreshScreen();
            }

            //End the audio
            Audio.CloseAudio();

            //Close any resources we were using
            Resources.ReleaseAllResources();
        }
    }
}